package com.aptemo.webetek.server.datastore;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.aptemo.webetek.client.enums.EnumBetStatus;
import com.aptemo.webetek.client.enums.EnumEventStatus;
import com.aptemo.webetek.client.enums.EnumPaymentOrdering;
import com.aptemo.webetek.client.enums.EnumPaymentType;
import com.aptemo.webetek.client.enums.EnumPickStatus;
import com.aptemo.webetek.client.enums.EnumTeamStatus;
import com.aptemo.webetek.client.exceptions.BackendException;
import com.aptemo.webetek.server.CommonService;
import com.aptemo.webetek.server.data.Bet;
import com.aptemo.webetek.server.data.PMF;
import com.aptemo.webetek.server.data.Payment;
import com.aptemo.webetek.server.data.Pick;
import com.aptemo.webetek.server.data.Stake;
import com.aptemo.webetek.server.data.UserGroup;
import com.aptemo.webetek.server.data.WebetekUser;
import com.aptemo.webetek.shared.EventBean;
import com.aptemo.webetek.shared.PaymentBean;
import com.aptemo.webetek.shared.TeamBean;
import com.aptemo.webetek.shared.UserBean;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public class DataStoreHelper
{
	private DataStoreAPI	dataService	= new DataStoreImpl();
	private CommonService	utils		= new CommonService();

	public List<String> getPicksForEvent(String userId, String betId) throws BackendException
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Stake.class);

		try
		{
			Key eventKey = KeyFactory.stringToKey(betId);
			Bet event = pm.getObjectById(Bet.class, eventKey);

			Key userKey = KeyFactory.stringToKey(userId);

			List<Pick> picks = event.getPicks();
			List<Key> pickKeys = new ArrayList<Key>();
			for (Pick item : picks)
			{
				pickKeys.add(item.getId());
			}
			//
			String filterString = "parentKey == :p1 && pickKeys == :p2";
			query.setFilter(filterString);

			List<Stake> qres = (List<Stake>) query.execute(userKey, pickKeys);

			List<String> result = new ArrayList<String>();
			for (Stake item : qres)
			{
				List<Key> keys = item.getPickKeys();
				for (Key kitem : keys)
				{
					if (pickKeys.contains(kitem))
					{
						String kstr = KeyFactory.keyToString(kitem);
						result.add(kstr);
					}
				}

			}

			return result;
		}
		finally
		{
			query.closeAll();
			pm.close();
		}
	}

	public EventBean assignResultPick(String pickkey, String comment)
	{

		Key winkey = KeyFactory.stringToKey(pickkey);

		PersistenceManager pm = PMF.get().getPersistenceManager();

		try
		{
			Bet event = pm.getObjectById(Bet.class, winkey.getParent());

			// set pick status
			List<Pick> picks = event.getPicks();
			for (Pick item : picks)
			{
				if (item.getId().equals(winkey))
				{
					// apply PICK status
					item.setStatus(EnumPickStatus.WON);
				}
				else
				{
					// apply PICK status
					item.setStatus(EnumPickStatus.LOOSE);
				}
			}

			// assign result comment and status
			event.setResultDescription(comment);
			event.setStatus(EnumEventStatus.CLOSED);

			return event.toBean();
		}
		finally
		{

			pm.close();
		}

	}

	public List<UserBean> completeStakes(String eventkey)
	{
		Key eventKey = KeyFactory.stringToKey(eventkey);

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{
			List<UserBean> users = new ArrayList<UserBean>();

			// TODO optimization - retrieve keys only
			Bet event = pm.getObjectById(Bet.class, eventKey);
			for (Pick item : event.getPicks())
			{
				users.addAll(completeStakesForPick(item.getId(), EnumBetStatus.OPENED));
			}

			return users;
		}
		finally
		{
			pm.close();
		}

	}

	private List<UserBean> completeStakesForPick(Key resultKey, EnumBetStatus status)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Query query = pm.newQuery(Stake.class);
		try
		{
			//
			String filterString = "pickKeys == :p2 && status == '" + status.toString() + "'";
			query.setFilter(filterString);
			List<Stake> qres = (List<Stake>) query.execute(resultKey);

			List<UserBean> users = new ArrayList<UserBean>();
			for (Stake bet : qres)
			{
				// traverse all picks...
				boolean betIncomplete = false;
				double earnings = 0.0;
				int wonCnt = 0;
				List<Key> keys = bet.getPickKeys();
				double amount = bet.getAmount() / keys.size();
				for (Key key : keys)
				{
					Pick pick = pm.getObjectById(Pick.class, key);
					if (pick.getStatus() == EnumPickStatus.NONE)
					{
						// not all bets (picks) closed...
						// reset earnings...
						bet.setEarnings(0.0);
						betIncomplete = true;
						break;
					}
					else if (pick.getStatus() == EnumPickStatus.WON)
					{
						// TODO problem - should be: bet.getAmount() /
						// picks.size()
						earnings += pick.getOdds() * amount;
						wonCnt++;
					}

				}

				// check other picks
				if (betIncomplete == false)
				{
					// update stake
					bet.setStatus(EnumBetStatus.CLOSED);

					// TODO error handling...
					if (Double.isInfinite(earnings) || Double.isNaN(earnings) || earnings < 0)
					{
						// log.severe("Wrong Stake earnings calculation!!! amount="
						// + stake.getAmount());
					}
					else
					{
						bet.setWonCnt(wonCnt);
						bet.setEarnings(earnings);
						// update user balance & earnings...
						if (earnings > 0.0)
						{
							WebetekUser user = pm.getObjectById(WebetekUser.class, bet.getId().getParent());

							Payment payment = new Payment(earnings, "Earnings calculation (balance=" + user.getBalance() + ")", EnumPaymentType.EARNINGS);
							user.addPayment(payment);

							// some stats...
							user.updateAllBetCount(keys.size());
							user.updateWonBetsCount(wonCnt);

							// collect users for response
							users.add(user.toBean());
						}
					}
				}

			}

			return users;
		}
		finally
		{
			query.closeAll();
			pm.close();

		}
	}

	/**
	 * Important! Storno earnings (payments) before calling this method
	 * 
	 * 
	 * @param pm
	 * @param user
	 * @throws BackendException
	 */
	public void completeStakesForUser(String userId) throws BackendException
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{

			// storno payments (earnings)
			List<PaymentBean> earningsList = dataService.getPayments(userId, EnumPaymentType.EARNINGS, EnumPaymentOrdering.DATE_ASC);
			for (PaymentBean item : earningsList)
			{
				dataService.deletePayment(item.getId());
			}

			WebetekUser user = pm.getObjectById(WebetekUser.class, userId);
			// reset user stats...
			user.resetBetCounters();

			// do calculation...
			for (Stake bet : user.getStakes())
			{
				// traverse all picks...
				boolean betIncomplete = false;
				double earnings = 0.0;
				int wonCnt = 0;
				List<Key> keys = bet.getPickKeys();
				double amount = bet.getAmount() / keys.size();
				for (Key key : keys)
				{
					Pick pick = pm.getObjectById(Pick.class, key);
					if (pick.getStatus() == EnumPickStatus.NONE)
					{
						// not all bets (picks) closed...
						// reset earnings...
						bet.setEarnings(0.0);
						betIncomplete = true;
						break;
					}
					else if (pick.getStatus() == EnumPickStatus.WON)
					{
						// TODO problem - should be: bet.getAmount() /
						// picks.size()
						earnings += pick.getOdds() * amount;
						wonCnt++;
					}
				}

				// check other picks
				if (betIncomplete == false)
				{
					// TODO error handling...
					if (Double.isInfinite(earnings) || Double.isNaN(earnings) || earnings < 0)
					{
						// log.severe("Wrong Stake earnings calculation!!! amount="
						// + stake.getAmount());
					}
					else
					{
						// update Bet
						bet.setWonCnt(wonCnt);
						bet.setEarnings(earnings);
						// some user stats...
						user.updateAllBetCount(keys.size());
						user.updateWonBetsCount(wonCnt);

						// update user balance & earnings...
						if (earnings > 0.0)
						{
							Payment payment = new Payment(earnings, "Earnings calculation (balance=" + user.getBalance() + ")", EnumPaymentType.EARNINGS);
							user.addPayment(payment);
						}
					}

					// update bet
					bet.setStatus(EnumBetStatus.CLOSED);
				}

			}
		}
		finally
		{
			pm.close();
		}
	}

	public void recalcUserCounters(String userId) throws BackendException
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{
			WebetekUser user = pm.getObjectById(WebetekUser.class, userId);
			// reset user stats...
			user.resetBetCounters();

			double investment = 0.0;
			// recalc investment
			for (Payment payment : user.getPayments())
			{
				if (payment.getType() == EnumPaymentType.BET)
				{
					investment += payment.getAmount();
				}
			}
			user.setInvestment(investment * (-1));

			// do calculation...
			double totalEarnings = 0.0;
			for (Stake bet : user.getStakes())
			{
				// traverse all picks...
				boolean betIncomplete = false;
				double earnings = 0.0;
				int wonCnt = 0;
				List<Key> keys = bet.getPickKeys();
				double amount = bet.getAmount() / keys.size();
				for (Key key : keys)
				{
					Pick pick = pm.getObjectById(Pick.class, key);
					if (pick.getStatus() == EnumPickStatus.NONE)
					{
						// not all bets (picks) closed...
						// reset earnings...
						bet.setEarnings(0.0);
						betIncomplete = true;
						break;
					}
					else if (pick.getStatus() == EnumPickStatus.WON)
					{
						// TODO problem - should be: bet.getAmount() /
						// picks.size()
						earnings += pick.getOdds() * amount;
						wonCnt++;
					}
				}

				// check other picks
				if (betIncomplete == false)
				{
					// TODO error handling...
					if (Double.isInfinite(earnings) || Double.isNaN(earnings) || earnings < 0)
					{
						// log.severe("Wrong Stake earnings calculation!!! amount="
						// + stake.getAmount());
					}
					else
					{
						// update Bet
						bet.setWonCnt(wonCnt);
						bet.setEarnings(earnings);
						// some user stats...
						user.updateAllBetCount(keys.size());
						user.updateWonBetsCount(wonCnt);
						totalEarnings += earnings;

						// update user balance & earnings...
						// if (earnings > 0.0)
						// {
						// Payment payment = new Payment(earnings,
						// "Earnings calculation (balance=" + user.getBalance()
						// + ")", EnumPaymentType.EARNINGS);
						// user.addPayment(payment);
						// }
					}

					// update bet
					// bet.setStatus(EnumBetStatus.CLOSED);
				}

			}
			user.setEarnings(totalEarnings);
		}
		finally
		{
			pm.close();
		}
	}

	public List<UserBean> getUsersForPick(String id)
	{
		List<UserBean> players = new ArrayList<UserBean>();

		PersistenceManager pm = PMF.get().getPersistenceManager();

		Query query = pm.newQuery(Stake.class);
		try
		{
			//
			String filterString = "pickKeys == :p";
			query.setFilter(filterString);
			Key pickKey = KeyFactory.stringToKey(id);
			List<Stake> qres = (List<Stake>) query.execute(pickKey);

			for (Stake bet : qres)
			{
				WebetekUser player = pm.getObjectById(WebetekUser.class, bet.getId().getParent());
				players.add(player.toBean());
			}
		}
		finally
		{
			query.closeAll();
			pm.close();

		}
		return players;
	}

	public TeamBean setTeamStatus(String teamId, EnumTeamStatus status)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{
			UserGroup team = pm.getObjectById(UserGroup.class, teamId);
			if (status != null)
			{
				team.setStatus(status);
				pm.makePersistent(team);
			}
			return team.toBean();
		}
		finally
		{
			pm.close();

		}

	}

}
